
"""
The <a href="http://en.wikipedia.org/wiki/SREC_%28file_format%29">S-rrecord
</a> was created by Motorola to represent the binrary data like Intel HEX.

It defines the record structure

<pre>
S Type  Byte Count Address Data Checksum
- ----  ---------- ------- ---- --------
</pre>

<ul>
  <li><b>Record type</b>, two characters with a uppercase "S" then a numeric \
      digit 0 to 9 to define the record type</li>
  <li><b>Byte Count</b>, two hex digits to indicate the number of bytes</li>
  <li><b>Address</b> four/six/eight hex digits as determined by the record \
      type</li>
  <li><b>Data</b> a sequence of 2n hex digits, for n bytes of data</li>
  <li><b>Checksum</b>, two hex digits</li>
</ul>
"""

__version__ = '1.07'

__aof__ = ('Motorola S-Record', 'SRC')
__extension__ = (('Motorola S-Record Files', ('*.srec', '*.m32')),)

_CHECK_CHECKSUM = 'Check s32 Checksum'

__configs__ = (
    {'name': _CHECK_CHECKSUM,
     'attr': 'bool',
     'default': False},
)


def _si(value):
    try:
        return int(value, 16)
    except ValueError:
        return -1


class _SrecDecorder:
    def Execute(self, source, output, notify, **kws):
        ret = False

        start = kws.get('start', 0)
        size = length = kws.get('length', len(source)) - start
        if length < 0:
            length = 0

        chksum = False
        if '_i' in kws:
            configs = kws['_i'].configs()
            chksum = configs.get(_CHECK_CHECKSUM, chksum)

        source.seek(start)

        o, ad = (0, 0)
        while length > 0:
            dat = ''
            while length > 0:
                c = source.read(1)
                length -= 1
                if c == '\r' or c == '\n':
                    break
                else:
                    dat += c

            if int(10 * length / size) == 10 * length / size:
                notify(100 * (size - length) / size, 'Parse S-record data ...')

            li = dat.strip()
            if len(li) == 0:
                continue
            elif len(li) < 10:
                # the minimum line should be: S9030000FC
                break
            elif li[0] != 'S':
                break

            ret = False
            # print 'li=%s' % li
            # ignore an empty line

            # check the checksum
            if chksum:
                checksum = 0
                for i in xrange(len(li) / 2 - 1):
                    o = 2 + 2 * i
                    checksum += _si(li[o:o + 2])

                if (checksum & 0xff) != 0xff:
                    break

            of = 0
            if li[1] == '0':
                # it's to indicate the header, ignore
                ret = True
                continue
            elif li[1] == '1' and len(li) >= 8:
                # it's a two-byte address data
                ad, of = _si(li[4:8]), 8
            elif li[1] == '2' and len(li) >= 10:
                # it's a three-byte address data
                ad, of = _si(li[4:10]), 10
            elif li[1] == '3' and len(li) >= 12:
                # it's a four-byte address data
                ad, of = _si(li[4:12]), 12
            elif li[1] == '5':
                # '5' - it's a counter, ignored here
                ret = True
                continue
            elif li[1] == '7' and len(li) >= 12:
                ret, ad = True, _si(li[4:12])
                continue
            elif li[1] == '8' and len(li) >= 10:
                ret, ad = True, _si(li[4:10])
                continue
            elif li[1] == '9' and len(li) >= 8:
                ret, ad = True, _si(li[4:8])
                continue

            if ad == -1:
                # wrong value returned by _si
                break

            o, ret = '', True
            for k in range(of, len(li) - 2, 2):
                ch = _si(li[k:k + 2])
                if ch == -1:
                    ret = False
                    break

                o += chr(ch)

            if ret:
                output.seek(ad)
                output.write(o)

        return ret


def cvtService():
    return _SrecDecorder
